package httpserver;

import java.io.*;
import java.net.Socket;
import java.util.*;
import java.util.logging.Level;

/**
 * Class for handling all HTTP responses from the server. 
 * @version 1.0
 * @author Casper, Jackie, Jacob og Dennis.
 */
public class Response implements Runnable{

    private String body;
    private String header;
    private File file;
    private String[] request;
    private Scanner input;
    private OutputStream output;
    private ConfigHandler cfgHandl;
    private Socket socket;
    private String htmlFile;
/**
 * Constructor for the response class. Initializes the needed streams,<br>
 * the configuration file to get the default page.
 * @param socket the given client socket that is wished to be given a response.
 * @throws FileNotFoundException
 * @throws IOException
 */
    public Response(Socket socket) throws FileNotFoundException, IOException {
        input = new Scanner(socket.getInputStream());
        output = socket.getOutputStream();
        cfgHandl = new ConfigHandler();
        this.socket = socket;
        request = input.nextLine().split(" ");
        if(request[1] == null || request[1].equalsIgnoreCase("/")){
            htmlFile = "/" + cfgHandl.getDefaultPage();
        }else{
            htmlFile = request[1];
        }        
        Server.LOGGER.info("Request recieved: "+request[0]+"  "+request[1]+ "  "+request[2]);
        methodType();
    }

    /**
     * Method to determine which HTTP method that is requested, and afterwards<br>
     * run either get(), post() or head().
     * @throws FileNotFoundException
     */
    private void methodType() throws FileNotFoundException {
        if (request[0].equalsIgnoreCase("GET")) {
            get();
        } else if (request[0].equalsIgnoreCase("POST")) {
            post();
        } else if (request[0].equalsIgnoreCase("HEAD")) {
            head();
        } else {
            status(400);
        }
    }

    /**
     *
     * @return HTTP 1.0 header.
     */
    private String getHeader() {
        return header;
    }

    /**
     *
     * @return The requested HTML as a String.
     */
    private  String getHtml() {
        return body;
    }

    /**
     *
     * @return The requested File as a file object.
     */
    public File getFile() {
        if (file != null) {
            file = new File(cfgHandl.getRoot() + htmlFile);
        }
        return file;
    }

    /**
     *
     * @return the file extension as a String, without a dot before it.
     */
    private String getContenType() {
        String filename = htmlFile;
        String[] tmp = filename.split("\\.");
        return tmp[tmp.length - 1];
    }

    /**
     * Method to check if the requested file exists in the rootdirectory.
     * @param filename, the filename that is requested by the client.
     * @return true if the file exists or false if the file doesn't exist.
     */
    private boolean exists(String filename) {
        String path = cfgHandl.getRoot() + filename;
        file = new File(path);
        return file.exists();
    }

    /**
     * Method to invoke whether status(200) if the file exists or status(404) if the file doesn't exist.
     * @throws FileNotFoundException
     */
    private void get() throws FileNotFoundException {        
        if (exists(htmlFile)) {
            status(200);
            Server.LOGGER.info("Responding with 200 OK");
        } else {
            status(404);
            Server.LOGGER.info("Responding with 404 Not Found on: "+htmlFile);
        }
    }

    /**
     * Invokes status(501) as this function is not yet implemented.
     * @throws FileNotFoundException
     */
    private void post() throws FileNotFoundException {
        status(501);
    }
    /**
     * Invokes status(501) as this function is not yet implemented.
     * @throws FileNotFoundException
     */
    private void head() throws FileNotFoundException {
        status(501);
    }

    /**
     * Returns the statuscode and html string (or file object) to the client.
     *
     * List of status codes:<br>
     * Status-Code    = "200"   ; OK<br>
     *                | "201"   ; Created<br>
     *                | "202"   ; Accepted<br>
     *                | "204"   ; No Content<br>
     *                | "301"   ; Moved Permanently<br>
     *                | "302"   ; Moved Temporarily<br>
     *                | "304"   ; Not Modified<br>
     *                | "400"   ; Bad Request<br>
     *                | "401"   ; Unauthorized<br>
     *                | "403"   ; Forbidden<br>
     *                | "404"   ; Not Found<br>
     *                | "500"   ; Internal Server Error<br>
     *                | "501"   ; Not Implemented<br>
     *                | "502"   ; Bad Gateway<br>
     *                | "503"   ; Service Unavailable<br>
     * @param code, the statuscode that is wished to be responded to the client.
     */
    private void status(int code) throws FileNotFoundException {
        String headerTmp = "";
        String html = "";

        switch (code) {
            case 200:
                headerTmp = "HTTP/1.0 200 OK\r\n\r\n";
                if (getContenType().equalsIgnoreCase("html") || getContenType().equalsIgnoreCase("htm")) {
                    Scanner scan = new Scanner(file);
                    while (scan.hasNext()) {
                        html += scan.nextLine();
                    }
                } else {
                    file = new File(cfgHandl.getRoot() + htmlFile);
                }
                break;
            case 201:
                headerTmp = "HTTP/1.0 201 CREATED\r\n\r\n";
                html = "<!DOCTYPE HTML PUBLIC \" -//IETF//DTD HTML 2.0//EN\">"
                        + "<html><head>"
                        + "<title>201 Created</title>"
                        + "</head><body>"
                        + "<h1>Created</h1>"
                        + "<p>------</p>"
                        + "</body></html>";
                break;
            case 202:
                headerTmp = "HTTP/1.0 202 ACCEPTED\r\n\r\n";
                html = "<!DOCTYPE HTML PUBLIC \" -//IETF//DTD HTML 2.0//EN\">"
                        + "<html><head>"
                        + "<title>202 Accepted</title>"
                        + "</head><body>"
                        + "<h1>Created</h1>"
                        + "<p>------</p>"
                        + "</body></html>";
                break;
            case 204:
                headerTmp = "HTTP/1.0 204 NO_CONTENT\r\n\r\n";
                html = "<!DOCTYPE HTML PUBLIC \" -//IETF//DTD HTML 2.0//EN\">"
                        + "<html><head>"
                        + "<title>204 No Content</title>"
                        + "</head><body>"
                        + "<h1>No Content</h1>"
                        + "<p>------</p>"
                        + "</body></html>";
                break;
            case 301:
                headerTmp = "HTTP/1.0 301 MOVED_PERMANENTLY\r\n\r\n";
                html = "<!DOCTYPE HTML PUBLIC \" -//IETF//DTD HTML 2.0//EN\">"
                        + "<html><head>"
                        + "<title>301 Moved Permanently</title>"
                        + "</head><body>"
                        + "<h1>Moved Permanently</h1>"
                        + "<p>------</p>"
                        + "</body></html>";
                break;
            case 302:
                headerTmp = "HTTP/1.0 302 MOVED_TEMPORARILY\r\n\r\n";
                html = "<!DOCTYPE HTML PUBLIC \" -//IETF//DTD HTML 2.0//EN\">"
                        + "<html><head>"
                        + "<title>302 Moved Temporarily</title>"
                        + "</head><body>"
                        + "<h1>Moved Temporarily</h1>"
                        + "<p>------</p>"
                        + "</body></html>";
                break;
            case 304:
                headerTmp = "HTTP/1.0 304 NOT_MODIFIED\r\n\r\n";
                html = "<!DOCTYPE HTML PUBLIC \" -//IETF//DTD HTML 2.0//EN\">"
                        + "<html><head>"
                        + "<title>304 Not Modified</title>"
                        + "</head><body>"
                        + "<h1>Not Modified</h1>"
                        + "<p>------</p>"
                        + "</body></html>";
                break;
            case 400:
                headerTmp = "HTTP/1.0 400 BAD_REQUEST\r\n\r\n";
                html = "<!DOCTYPE HTML PUBLIC \" -//IETF//DTD HTML 2.0//EN\">"
                        + "<html><head>"
                        + "<title>400 Bad Request</title>"
                        + "</head><body>"
                        + "<h1>Bad Request</h1>"
                        + "<p>------</p>"
                        + "</body></html>";
                break;
            case 401:
                headerTmp = "HTTP/1.0 401 UNAUTHORIZED\r\n\r\n";
                html = "<!DOCTYPE HTML PUBLIC \" -//IETF//DTD HTML 2.0//EN\">"
                        + "<html><head>"
                        + "<title>401 Unauthorized</title>"
                        + "</head><body>"
                        + "<h1>Unauthorized</h1>"
                        + "<p>------</p>"
                        + "</body></html>";
                break;
            case 403:
                headerTmp = "HTTP/1.0 403 FORBIDDEN\r\n\r\n";
                html = "<!DOCTYPE HTML PUBLIC \" -//IETF//DTD HTML 2.0//EN\">"
                        + "<html><head>"
                        + "<title>403 Forbidden</title>"
                        + "</head><body>"
                        + "<h1>Forbidden</h1>"
                        + "<p>------</p>"
                        + "</body></html>";
                break;
            case 404:
                headerTmp = "HTTP/1.0 404 NOT_FOUND\r\n\r\n";
                html = "<!DOCTYPE HTML PUBLIC \" -//IETF//DTD HTML 2.0//EN\">"
                        + "<html><head>"
                        + "<title>404 Not Found</title>"
                        + "</head><body>"
                        + "<h1>Not Found</h1>"
                        + "<p>The file " + htmlFile + " was not found on the server.</p>"
                        + "</body></html>";
                break;
            case 500:
                headerTmp = "HTTP/1.0 500 INTERNAL_SERVER_ERROR\r\n\r\n";
                html = "<!DOCTYPE HTML PUBLIC \" -//IETF//DTD HTML 2.0//EN\">"
                        + "<html><head>"
                        + "<title>500 Internal Server Error</title>"
                        + "</head><body>"
                        + "<h1>Internal Server Error</h1>"
                        + "<p>------</p>"
                        + "</body></html>";
                break;
            case 501:
                headerTmp = "HTTP/1.0 501 NOT_IMPLEMENTED\r\n\r\n";
                html = "<!DOCTYPE HTML PUBLIC \" -//IETF//DTD HTML 2.0//EN\">"
                        + "<html><head>"
                        + "<title>501 Not Implemented</title>"
                        + "</head><body>"
                        + "<h1>Not Implemented</h1>"
                        + "<p>------</p>"
                        + "</body></html>";
                break;
            case 502:
                headerTmp = "HTTP/1.0 502 BAD_GATEWAY\r\n\r\n";
                html = "<!DOCTYPE HTML PUBLIC \" -//IETF//DTD HTML 2.0//EN\">"
                        + "<html><head>"
                        + "<title>502 Bad Gateway</title>"
                        + "</head><body>"
                        + "<h1>Bad Gateway</h1>"
                        + "<p>------</p>"
                        + "</body></html>";
                break;
            case 503:
                headerTmp = "HTTP/1.0 503 SERVICE_UNAVAILABLE\r\n\r\n";
                html = "<!DOCTYPE HTML PUBLIC \" -//IETF//DTD HTML 2.0//EN\">"
                        + "<html><head>"
                        + "<title>503 Service Unavailable</title>"
                        + "</head><body>"
                        + "<h1>Service Unavailable</h1>"
                        + "<p>------</p>"
                        + "</body></html>";
                break;
            default:
                status(500);
                break;
        }
        body = html;
        header = headerTmp;
    }

    /**
     * NYI.
     * @param path
     * @return
     */
    private ArrayList folderMapping(String path){
        ArrayList list = new ArrayList();
        return list;
    }

    /**
     * Abstract method implemented from the runnable interface, used to enable <br>
     * the class to run in a thread. This method also handles the outputstream <br>
     * to the clientsocket, and finally it closes the clientsocket.
     */
    public void run() {
        try {
            if (getContenType().equalsIgnoreCase("html") || getContenType().equalsIgnoreCase("htm")) {
                output.write(getHeader().getBytes(), 0, getHeader().length());
                output.write(getHtml().getBytes(), 0, getHtml().length());
                output.flush();
            } else{
                FileInputStream fs = new FileInputStream(getFile());
                final byte[] buffer = new byte[1024];
                int byteRead = fs.read(buffer);
                output.write(getHeader().getBytes(), 0, getHeader().length());
                while (byteRead != -1) {
                    output.write(buffer, 0, byteRead);
                    byteRead = fs.read(buffer);
                }
            }
        } catch (FileNotFoundException ex) {
            Server.LOGGER.log(Level.WARNING, "FileNotFoundException catched in Response", ex);
        } catch (IOException ex) {
            Server.LOGGER.log(Level.WARNING, "IOException catched in Response", ex);
        } finally {
            try {
                socket.close();
            } catch (IOException ex) {
                Server.LOGGER.log(Level.WARNING, "Exception on closing socket!",ex);
            }
        }
    }
}